<?php
namespace Core\Libs\Validation;

class Validation {

	private $value = NULL;
	private $regex = '';
	private $deep = FALSE;
	private $country = NULL;
	private $type = NULL;
	private $pattern = array('hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)');
	private $casting = TRUE;

	public function __construct($method, $params = array(), $auto = FALSE) {

		$this->value = NULL;
		$this->regex = '';
		$this->deep = FALSE;
		$this->country = NULL;
		$this->type = NULL;
		$this->casting = TRUE;

		if($auto) {

			$this->methodCall($method, $params);
		}
	}

	final public function methodCall($method, $params = array()) {

		$noCall = array('methodCall', '__construct', 'setRegex', 'getRegex', 'setCountry', 'getCountry', 'addPattern', 'getPatterns', 'getCast', 'extract');

		if(method_exists($this, $method) && !in_array($method, $noCall)) {

			try {

				return call_user_func_array(array($this, $method), $params);
			}

			catch(\Exception $e) {

				return FALSE;
			}
		}

		return FALSE;
	}

	final public function setRegex($regex = NULL) {

		if(!empty($regex)) {

			$this->regex = $regex;
		}

		return $this;
	}

	final public function getRegex() {

		return $this->regex;
	}

	final public function setCountry($country = NULL) {

		if(!empty($country)) {

			$this->country = $country;
		}

		return $this;
	}

	final public function getCountry() {

		return $this->country;
	}

	final public function addPattern($id = NULL, $regex = NULL) {

		if(!empty($id) && !empty($regex)) {

			$this->pattern[$id] = $regex;
		}
	}

	final public function getPatterns() {

		return $this->pattern;
	}

	private function getCast($param = NULL) {

		if(func_num_args() > 0) {

			return \Core\Libs\MasterObject\MasterObject::getCast($param);
		}

		else {

			return \Core\Libs\MasterObject\MasterObject::getCast();
		}
	}

	private function extract($params) {

		extract($params, EXTR_OVERWRITE);

		if(isset($check)) {

			$this->check = $check;
		}

		if(isset($regex)) {

			$this->regex = $regex;
		}

		if(isset($country)) {

			$this->country = mb_strtolower($country);
		}

		if(isset($deep)) {

			$this->deep = $deep;
		}

		if(isset($type)) {

			$this->type = $type;
		}
	}

	final public function between($check, $min, $max) {

		$length = mb_strlen($check);
		return ($length >= $min && $length <= $max);
	}

	final public function alphaNumeric($check) {

		$this->check = $check;

		if(is_array($check)) {

			$this->extract($check);
		}

		if(empty($this->check)) {

			return FALSE;
		}

		$this->regex = '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/mu';
		return $this->check();
	}

	final public function blank($check) {

		$this->check = $check;

		if(is_array($check)) {

			$this->extract($check);
		}

		$this->regex = '/[^\\s]/';
		return !$this->check();
	}

	final public function date($check, $format = 'ymd', $regex = NULL) {

		$this->check = $check;
		$this->regex = $regex;

		if (!empty($this->regex)) {

			return $this->check();
		}

		$regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.|\\x20)(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.|\\x20)0?2\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.|\\x20)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
		$regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])(\\/|-|\\.|\\x20)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:0?2(\\/|-|\\.|\\x20)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.|\\x20)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
		$regex['ymd'] = '%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.|\\x20)(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})(\\/|-|\\.|\\x20)(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%';
		$regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ (((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ((1[6-9]|[2-9]\\d)\\d{2})$/';
		$regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))))\\,?\\ ((1[6-9]|[2-9]\\d)\\d{2}))$/';
		$regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)[ /]((1[6-9]|[2-9]\\d)\\d{2})$%';
		$regex['my'] = '%^(((0[123456789]|10|11|12)([- /.])(([1][9][0-9][0-9])|([2][0-9][0-9][0-9]))))$%';

		$format = (is_array($format)) ? array_values($format) : array($format);

		foreach ($format as $key) {

			$this->regex = $regex[$key];

			if ($this->check() === TRUE) {

				return TRUE;
			}
		}

		return FALSE;
	}

	function time($check) {

		$this->check = $check;
		$this->regex = '%^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%';

		return $this->check();
	}

	function range($check, $lower = NULL, $upper = NULL) {

		if(!is_numeric($check)) {

			return FALSE;
		}

		if(isset($lower) && isset($upper)) {

			return ($check > $lower && $check < $upper);
		}

		return is_finite($check);
	}

	final public function minLength($check, $min) {

		$length = mb_strlen($check);
		return ($length >= $min);
	}

	final public function maxLength($check, $max) {

		$length = mb_strlen($check);
		return ($length <= $max);
	}

	final public function multiple($check, $options = array()) {

		$defaults = array('in' => NULL, 'max' => NULL, 'min' => NULL);
		$options = array_merge($defaults, $options);
		$check = array_filter((array)$check);

		if(empty($check)) {

			return FALSE;
		}

		if($options['max'] && count($check) > $options['max']) {

			return FALSE;
		}

		if($options['min'] && count($check) < $options['min']) {

			return FALSE;
		}

		if($options['in'] && is_array($options['in'])) {

			foreach($check as $val) {

				if(!in_array($val, $options['in'])) {

					return FALSE;
				}
			}
		}

		return TRUE;
	}

	final public function boolean($check) {

		try {

			$this->getCast()->bool($check);
			return TRUE;
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function int($check) {

		try {

			$this->getCast()->int($check);
			return TRUE;
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function numeric($check, $cents = NULL, $thousands = NULL, $currency = NULL) {

		try {

			if(func_num_args() < 2) {

				$this->getCast()->numeric($check);
				return TRUE;
			}

			else {

				$this->getCast()->numeric($check, $cents, $thousands, $currency);
				return TRUE;
			}
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function decimal($check) {

		try {

			$this->getCast()->decimal($check);
			return TRUE;
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function double($check) {

		try {

			$this->getCast()->double($check);
			return TRUE;
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function float($check) {

		try {

			$this->getCast()->float($check);
			return TRUE;
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function moneyOfCountry($check = NULL, $country = 'pt_BR') {

		try {

			$this->getCast()->setCountry($country)->money($check);
			return TRUE;
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function money($check = NULL, $cents = NULL, $thousands = NULL, $currency = NULL, $precision = 2) {

		try {

			if(func_num_args() < 2) {

				$this->getCast()->money($check);
				return TRUE;
			}

			else {

				$this->getCast()->money($check, $cents, $thousands, $currency, $precision);
				return TRUE;
			}
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function equalTo($check) {

		try {

			return $this->getCast()->bool($check);
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function compare($arg1 = NULL, $operator = '!=', $arg2 = NULL, $reference = FALSE) {

		$operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower($operator));

		try {

			return $this->getCast()->compare($arg1, $operator, $arg2, $reference);
		}

		catch(\Exception $e) {

			return FALSE;
		}
	}

	final public function equals() {

		
	}

	final public function uuid($check) {

		$this->check = $check;
		$this->regex = '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i';

		return $this->check();
	}

	final public function url($check, $strict = false) {

		$this->ipPattern();
		$this->check = $check;
		$validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z]|(%[0-9a-f]{2}))';
		$this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') .
			'(?:' . $this->pattern['IPv4'] . '|\[' . $this->pattern['IPv6'] . '\]|' . $this->pattern['hostname'] . ')' .
			'(?::[1-9][0-9]{0,4})?' .
			'(?:\/?|\/' . $validChars . '*)?' .
			'(?:\?' . $validChars . '*)?' .
			'(?:#' . $validChars . '*)?$/i';

		return $this->check();
	}

	private function ipPattern() {

		if(!isset($this->pattern['IPv6'])) {

			$pattern  = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}';
			$pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})';
			$pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})';
			$pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)';
			$pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
			$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}';
			$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|';
			$pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}';
			$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
			$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})';
			$pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)';
			$pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]';
			$pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})';
			$pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?';

			$this->pattern['IPv6'] = $pattern;
		}

		if(!isset($this->pattern['IPv4'])) {

			$pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])';
			$this->pattern['IPv4'] = $pattern;
		}
	}

	final public function inList($check, $list) {

		if(!is_array($list)) {

			return FALSE;
		}

		return in_array($check, $list);
	}

	final public function email($check, $deep = FALSE, $regex = NULL) {

		$this->check = $check;
		$this->regex = $regex;
		$this->deep = $deep;

		if(is_array($check)) {

			$this->extract($check);
		}

		if(empty($_this->regex)) {

			$_this->regex = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . $_this->pattern['hostname'] . '$/i';
		}

		$return = $this->check();

		if($this->deep === FALSE || $this->deep === NULL) {

			return $return;
		}

		if($return === TRUE && preg_match('/@(' . $this->pattern['hostname'] . ')$/i', $this->check, $regs)) {

			if(function_exists('getmxrr')) {

				return getmxrr($regs[1], $mxhosts);
			}

			if(function_exists('checkdnsrr')) {

				return checkdnsrr($regs[1], 'MX');
			}

			return is_array(gethostbynamel($regs[1]));
		}

		return FALSE;
	}

	final public function custom($check = NULL, $regex = NULL) {

		$this->check = $check;
		$this->regex = $regex;

		if(is_array($check)) {

			$this->extract($check);
		}

		if(empty($this->regex)) {

			return FALSE;
		}

		return $this->check();
	}

	final public function check() {

		if(preg_match($this->regex, $this->check)) {

			$this->errors[] = FALSE;
			return TRUE;
		}

		else {

			$this->errors[] = TRUE;
			return FALSE;
		}
	}
}

class CheckError {

	
}
?>